module axi4_crtl(
    input ACLK,
    input ARESETn,
    input flush,

    input inst_r_ena,
    input [63:0] inst_addr,
    output [63:0] inst_r,
    input data_ena,
    input [63:0] data_w,
    input [63:0] data_addr,
    input [7:0] data_w_mask,
    input wlast_i,
    output [63:0] data_r,

    output [3:0] ARID,
    output [63:0] ARADDR,
    output ARVALID,
    input ARREADY,

    input [3:0] RID,
    input [63:0] RDATA,
    input RVALID,
    output RREADY,
    input RLAST,

    output [3:0] AWID,
    output [63:0] AWADDR,
    output AWVALID,
    input AWREADY,

    output [63:0] WDATA,
    output [7:0] WSTRB,
    output WVALID,
    input WREADY,
    output WLAST,

    input [3:0] BID,
    input BVALID,
    output BREADY
);

    parameter IDEL               = 3'b000;	//空闲
	parameter INST_SEND_RADDR    = 3'b001;	//指令cache发读地址
	parameter INST_RECEIVE_RDATA = 3'b010;	//指令cache接收读数据
	parameter DATA_SEND_RADDR    = 3'b011;	//数据cache发读地址
	parameter DATA_RECEIVE_RDATA = 3'b100;	//数据cache接收读数据
	parameter DATA_SEND_WADDR    = 3'b001;	//数据cache发写地址
	parameter DATA_SEND_WDATA    = 3'b010;	//数据cache发写数据
	parameter DATA_RECEIVE_B     = 3'b011;	//数据cache写响应

    wire ar_hs = ARREADY & ARVALID;
    wire r_hs = RREADY & RVALID;
    wire aw_hs = AWREADY & AWVALID;
    wire w_hs = WREADY & WVALID;
    wire b_hs = BREADY & BVALID;
    wire r_done = r_hs & RLAST;
    wire w_done = w_hs & WLAST;
	
	reg [2:0] axi_read_state;
    reg [2:0] axi_write_state;

    always @(posedge ACLK) begin
        if(ARESETn) begin
            axi_read_state <= 3'b000;
        end else begin
            case(axi_read_state)
                IDEL: begin
                    if(flush) begin
                        axi_read_state <= IDEL;
                    end else if(inst_r_ena) begin
                        axi_read_state <= INST_SEND_RADDR;
                    end else if(data_ena) begin
                        if(data_w_mask == 8'b00000000) begin
                            axi_read_state <= DATA_SEND_RADDR;
                        end
                    end
                end
                INST_SEND_RADDR : begin
                    if(ar_hs) begin
                        axi_read_state <= INST_RECEIVE_RDATA;
                    end
                end
                INST_RECEIVE_RDATA : begin
                    if(r_done) begin
                        axi_read_state <= IDEL;
                    end
                end
                DATA_SEND_RADDR : begin
                    if(ar_hs) begin
                        axi_read_state <= DATA_RECEIVE_RDATA;
                    end
                end
                DATA_RECEIVE_RDATA : begin
                    if(r_done) begin
                        axi_read_state <= IDEL;
                    end
                end
            endcase
        end
    end

    always @(posedge ACLK) begin
        if(ARESETn) begin
            axi_write_state <= 3'b000;
        end else begin
            case(axi_write_state) begin
                IDEL: begin
                    if(flush) begin
                        axi_write_state <= IDEL;
                    end else if(data_ena) begin
                        if(data_w_mask != 8'b00000000) begin
                            axi_write_state <= DATA_SEND_WADDR;
                        end
                    end
                end
                DATA_SEND_WADDR : begin
                    if(aw_hs) begin
                        axi_write_state <= DATA_SEND_WDATA;
                    end
                end
                DATA_SEND_WDATA : begin
                    if(w_done) begin
                        axi_write_state <= DATA_RECEIVE_B;
                    end
                end
                DATA_RECEIVE_B : begin
                    if(b_hs) begin
                        axi_write_state <= IDEL;
                    end
                end
            endcase    
        end
    end

    assign inst_r = (axi_read_state == INST_RECEIVE_RDATA) ? RDATA : 64'd0;
    assign data_r = (axi_read_state == DATA_RECEIVE_RDATA) ? RDATA : 64'd0;

    assign ARID = (axi_read_state == INST_SEND_RADDR) ? 4'b0001 : (axi_read_state == DATA_SEND_RADDR ? 4'b0010 : 4'b0000);
    assign ARADDR = (axi_read_state == INST_SEND_RADDR) ? inst_addr : (axi_read_state == DATA_SEND_RADDR ? data_addr : 64'd0);
    assign ARVALID = (axi_read_state == INST_SEND_RADDR || axi_read_state == DATA_SEND_RADDR) ? 1'b1 : 1'b0;
    assign RREADY = (axi_read_state == INST_RECEIVE_RDATA || axi_read_state == DATA_RECEIVE_RDATA) ? 1'b1 : 1'b0;
    assign AWID = (axi_write_state == DATA_SEND_WADDR) ? 4'b0001 : 4'b0000;
    assign AWADDR = (axi_write_state == DATA_SEND_WADDR) ? data_addr : 64'd0;
    assign WDATA = (axi_write_state == DATA_SEND_WDATA) ? data_w : 64'd0;
    assign WSTRB = (axi_write_state == DATA_SEND_WDATA) ? data_w_mask : 8'b00000000;
    assign WVALID = (axi_write_state == DATA_SEND_WDATA) ? 1'b1 : 1'b0;
    assign WLAST = wlast_i;
    assign BREADY = (axi_write_state == DATA_RECEIVE_B) ? 1'b1 : 1'b0;
endmodule